package com.gcloud.mesh.framework.core.mq.processor;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;

import com.gcloud.mesh.framework.core.SpringUtil;
import com.gcloud.mesh.framework.core.init.IInitor;
import com.gcloud.mesh.framework.core.init.InitPriority;
import com.gcloud.mesh.framework.core.msg.MsgResolver;
import com.gcloud.mesh.header.exception.BaseException;
import com.gcloud.mesh.header.exception.CommonErrorCode;
import com.gcloud.mesh.header.msg.MqBaseMsg;
import com.gcloud.mesh.header.msg.MqBaseReplyMsg;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
@ConditionalOnProperty(name = "mesh.mq.enable", havingValue = "true")
public class MsgProcessors implements IInitor {
	
	private static final Map<String, IMsgProcessor> PROCESSORS = new HashMap<>();
    private static final Map<String, Class> MSG_CLASSES = new HashMap<>();
    
    @Override
    public InitPriority initPriority() {
        return InitPriority.AFTER_ROLE_INIT;
    }
    
    public void init() throws Exception {
        log.info("[MsgProcessor] processor number: {}", SpringUtil.getBeansOfType(IMsgProcessor.class).size());
        for (Map.Entry<String, IMsgProcessor> entry : SpringUtil.getBeansOfType(IMsgProcessor.class).entrySet()) {
            String action = entry.getKey();
            IMsgProcessor processor = entry.getValue();
            action = action.substring(0, 1).toUpperCase() + action.substring(1, action.length() - "MsgProcessor".length());
            PROCESSORS.put(action, processor);
            log.info("[MsgProcessor] register MsgProcessor {} {}", action, processor.getClass());

            for (Type i : processor.getClass().getGenericInterfaces()) {
                if (i.getTypeName().split("<")[0].equals("com.gcloud.mesh.framework.core.mq.processor.IMsgProcessor")) {
                    Type[] t = ((ParameterizedType)i).getActualTypeArguments();
                    MSG_CLASSES.put(action, (Class)t[0]);
                    MSG_CLASSES.put(action + "Reply", (Class)t[1]);
                    log.info("[MsgProcessor] register Msg Class {} {}", action, ((Class)t[0]).getCanonicalName());
                    break;
                }
            }
        }
    }
    
    public static IMsgProcessor getProcessor(String action) {
        return PROCESSORS.get(action);
    }

    public static void registerMsgClass(Class msgClass) {
        String name = msgClass.getSimpleName();
        MSG_CLASSES.put(name.substring(0, name.length() - 3), msgClass);
    }

    public static Class getMsgClass(String action) {
        return MSG_CLASSES.get(action);
    } 

    public static MqBaseReplyMsg process(MqBaseMsg msg) {
        return MsgProcessors.process(MsgResolver.getAction(msg.getClass()), msg);
    }

    public static MqBaseReplyMsg process(String action, MqBaseMsg msg) {
        IMsgProcessor processor = PROCESSORS.get(action);
        if (processor == null) {
            throw new BaseException(CommonErrorCode.PROCESSOR_NOT_FOUND);
        }
        return processor.process(msg);
    }
	
}
